package com.sui.bigdata.sml.web.configuration;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.feidee.fdcommon.util.TimeUtils;
import com.sui.bigdata.sml.web.dto.ModelDTO;
import com.sui.bigdata.sml.web.dto.ModelGroupDTO;
import com.sui.bigdata.sml.web.dto.ModelGroupInfoDTO;
import com.sui.bigdata.sml.web.misc.k8s.ServingModel;
import com.sui.bigdata.sml.web.repository.model.FieldInfo;
import com.sui.bigdata.sml.web.repository.model.Model;
import com.sui.bigdata.sml.web.repository.model.ModelGroup;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.converter.BidirectionalConverter;
import ma.glasnost.orika.converter.ConverterFactory;
import ma.glasnost.orika.metadata.Type;
import net.rakugakibox.spring.boot.orika.OrikaMapperFactoryConfigurer;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @author songhaicheng
 * @date 2019/11/5 16:07
 * @description 自定义 Model 到 DTO 的转换器
 * @reviewer
 */
@Configuration
public class SmlOrikaMapperFactoryConfig implements OrikaMapperFactoryConfigurer {

    @Override
    public void configure(MapperFactory orikaMapperFactory) {
        ConverterFactory factory = orikaMapperFactory.getConverterFactory();
        factory.registerConverter(new Date2StringConverter());
        factory.registerConverter(new String2FieldInfoList());
        factory.registerConverter(new StringWithComma2List());

        // 将 model 表的“字段信息”字符串值映射成对应 DTO 里的对象列表
        orikaMapperFactory.classMap(Model.class, ModelDTO.class)
                .field("fieldInfos", "fields")
                .field("modelTypes", "modelType")
                .byDefault()
                .register();
        // 将 model_group 表的模型转成 sml-model 实时预测服务的模型实体
        orikaMapperFactory.classMap(Model.class, ServingModel.class)
                .field("fieldInfos", "warmUpFeature")
                .field("modelTypes", "modelType")
                .byDefault()
                .register();
        // 将 model_group 表的模型 token 信息做格式转换
        orikaMapperFactory.classMap(ModelGroup.class, ModelGroupDTO.class)
                .field("modelTokens", "models")
                .field("servingToken", "token")
                .byDefault()
                .register();
        orikaMapperFactory.classMap(ModelGroup.class, ModelGroupInfoDTO.class)
                .field("servingToken", "token")
                .byDefault()
                .register();
        orikaMapperFactory.classMap(ModelGroupDTO.class, ModelGroup.class)
                .field("models", "modelTokens")
                .field("token", "servingToken")
                .byDefault()
                .register();
    }

    /**
     * 将从数据库查出的 Date 类型转成通用的日期字符串
     */
    static class Date2StringConverter extends BidirectionalConverter<Date, String> {

        @Override
        public String convertTo(Date date, Type<String> type, MappingContext mappingContext) {
            return TimeUtils.millisToTime(date.getTime());
        }

        @Override
        public Date convertFrom(String s, Type<Date> type, MappingContext mappingContext) {
            // 一般来说，数据库 Date 类型字段由数据库约束自己修改
            return null;
        }
    }

    static class String2FieldInfoList extends BidirectionalConverter<String, List<FieldInfo>> {

        @Override
        public List<FieldInfo> convertTo(String s, Type<List<FieldInfo>> type, MappingContext mappingContext) {
            try {
                return JSON.parseObject(s, new TypeReference<List<FieldInfo>>() {});
            } catch (Exception e) {
                return null;
            }
        }

        @Override
        public String convertFrom(List<FieldInfo> fieldInfos, Type<String> type, MappingContext mappingContext) {
            return JSON.toJSONString(fieldInfos);
        }
    }

    static class StringWithComma2List extends BidirectionalConverter<String, List<String>> {

        @Override
        public List<String> convertTo(String s, Type<List<String>> type, MappingContext mappingContext) {
            return Arrays.asList(s.split(","));
        }

        @Override
        public String convertFrom(List<String> strings, Type<String> type, MappingContext mappingContext) {
            return String.join(",", strings);
        }
    }
}

